﻿using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Threading.Tasks;
using VisionCloud.Domain.Common;

namespace VisionCloud.Domain.Extension
{
    /// <summary>
    /// ef拓展类
    /// </summary>
    public static class EFExtension
    {
        /// <summary>
        /// 添加where条件
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="query"></param>
        /// <param name="useWhere">如果非false,不添加 </param>
        /// <param name="whereExpression"></param>
        /// <returns></returns>
        public static IQueryable<T> WhereIf<T>(this IQueryable<T> query, bool useWhere, Expression<Func<T, bool>> whereExpression)
        {
            return useWhere ? query.Where(whereExpression) : query;
        }

        /// <summary>
        /// 设置默认排序条件 正序
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="TKey"></typeparam>
        /// <param name="query"></param>
        /// <param name="page"></param>
        /// <param name="orderExpression"></param>
        /// <returns></returns>
        public static IQueryable<T> DefaultOrderBy<T, TKey>(this IQueryable<T> query, PageBase page, Expression<Func<T, TKey>> orderExpression)
        {
            return !string.IsNullOrWhiteSpace(page.OrderBy) ? query : query.OrderBy(orderExpression);
        }

        /// <summary>
        /// 设置默认排序条件 反序
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="TKey"></typeparam>
        /// <param name="query"></param>
        /// <param name="page"></param>
        /// <param name="orderExpression"></param>
        /// <returns></returns>
        public static IQueryable<T> DefaultOrderByDescending<T, TKey>(this IQueryable<T> query, PageBase page, Expression<Func<T, TKey>> orderExpression)
        {
            return !string.IsNullOrWhiteSpace(page.OrderBy) ? query : query.OrderByDescending(orderExpression);
        }

        /// <summary>
        /// 分页
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="query"></param>
        /// <param name="page">分页参数</param>
        /// <returns></returns>
        public static async Task<List<T>> ToPageList<T>(this IQueryable<T> query, PageBase page)
        {
            if (query.GetType().AssemblyQualifiedName.Contains("System.Linq"))// 为了兼容不实现Provider的集合操作
                return ToPageList(query.AsEnumerable(), page).Result.ToList();
            page.Total = await query.CountAsync();
            if (!string.IsNullOrWhiteSpace(page.OrderBy))
            {
                var orders = page.OrderBy.Split(",");
                int flag = 0;
                IOrderedQueryable<T> orderQuery = null;
                foreach (var order in orders)
                {
                    var items = order.Split(" ");
                    var propertyName = items[0].Trim();
                    var parameter = Expression.Parameter(typeof(T));
                    var property = Expression.Property(parameter, propertyName);
                    var expression = Expression.Lambda<Func<T, dynamic>>(Expression.Convert(property, typeof(object)), parameter);
                    if (orders.Count() > 1 && flag > 0)
                        orderQuery = items[1].Equals("asc", StringComparison.OrdinalIgnoreCase) ? orderQuery.ThenBy(expression) : orderQuery.ThenByDescending(expression);
                    else
                        orderQuery = items[1].Equals("asc", StringComparison.OrdinalIgnoreCase) ? query.OrderBy(expression) : query.OrderByDescending(expression);
                    flag++;
                }
                query = orderQuery ?? query;
            }

            if (page.CurrentPage > page.TotalPage)
                page.CurrentPage = page.TotalPage;
            if (page.CurrentPage < 1)
                page.CurrentPage = 1;

            if (page.PageSize == int.MaxValue)  //特殊逻辑 ,PageSize 为最大值是,返回所有
            {
                page.CurrentPage = 1;
                return await query.ToListAsync();
            }
            return await query.Skip((page.CurrentPage - 1) * page.PageSize).Take(page.PageSize).ToListAsync();
        }

        private static object GetPropertyValue(object obj, string property)
        {
            PropertyInfo propertyInfo = obj.GetType().GetProperty(property);
            return propertyInfo.GetValue(obj, null);
        }

        private static IOrderedEnumerable<T> Order<T>(bool isDescSort, IOrderedEnumerable<T> list, string propertyName)
        {
            list = isDescSort ?
                list.ThenByDescending(p => GetPropertyValue(p, propertyName))
                : list.ThenBy(p => GetPropertyValue(p, propertyName));
            return list;
        }

        public static async Task<IEnumerable<T>> ToPageList<T>(this IEnumerable<T> query, PageBase page)
        {
            page.Total = query.Count();
            List<T> list = null;
            //动态添加排序条件
            if (!string.IsNullOrWhiteSpace(page.OrderBy))
            {
                var orders = page.OrderBy.Split(",");
                var firstOrderBy = true;

                foreach (var order in orders)
                {
                    var items = order.Split(" ");
                    var propertyName = items[0];

                    bool isDescSort = items.Length > 1 &&
                                      items[1].Trim().Equals("desc", StringComparison.OrdinalIgnoreCase);

                    if (firstOrderBy)
                    {
                        list = isDescSort
                            ? query.OrderByDescending(p => GetPropertyValue(p, propertyName)).ToList()
                            : query.OrderBy(p => GetPropertyValue(p, propertyName)).ToList();
                    }
                    else
                    {
                        list = Order(isDescSort, (IOrderedEnumerable<T>)list, propertyName).ToList();
                    }
                    firstOrderBy = false;
                }
            }
            else
            {
                list = query.ToList();
            }

            if (page.CurrentPage > page.TotalPage)
                page.CurrentPage = page.TotalPage;
            if (page.CurrentPage < 1)
                page.CurrentPage = 1;
            if (page.PageSize == int.MaxValue)   //特殊逻辑 ,PageSize 为最大值是,返回所有
            {
                page.CurrentPage = 1;
                return list ?? query;
            }
            return list.Skip((page.CurrentPage - 1) * page.PageSize).Take(page.PageSize).ToList();
        }

        /// <summary>
        /// 获取DataSet的方法
        /// </summary>
        /// <param name="db"></param>
        /// <param name="commandType"></param>
        /// <param name="commandText"></param>
        /// <param name="parameterValues"></param>
        /// <returns></returns>
        public static DataSet GetDataSet(this Core.BlueDbContext db, CommandType commandType, string commandText, params DbParameter[] parameterValues)
        {
            DbConnection con = db.Database.GetDbConnection();//DbConnection是SqlConnection的父类(抽象型类)，其余ADO对象与此情况类同

            using (DbCommand cmd = con.CreateCommand())//DbCommand是个抽象类，C#为了让接口能正常运行，就设置了这样一个方法来生成接口对象,也算是无奈情况下的一个特例吧，毕竟不能new,却还必须得用
            {
                cmd.CommandText = commandText;
                cmd.CommandType = commandType;
                cmd.Parameters.AddRange(parameterValues);

                DataSet ds = new DataSet();
                using (var sda = DbProviderFactories.GetFactory(con).CreateDataAdapter())
                {
                    sda.SelectCommand = cmd;
                    sda.Fill(ds);
                }

                return ds;
            }
        }

        /// <summary>
        /// 获取新的连接对象
        /// </summary>
        /// <param name="db"></param>
        /// <param name="serverIp"></param>
        /// <param name="serverPort"></param>
        /// <param name="dbName"></param>
        /// <param name="dbUser"></param>
        /// <param name="dbPwd"></param>
        /// <returns></returns>
        public static DbConnection GetNewConn(this Core.BlueDbContext db, string serverIp, string serverPort, string dbName, string dbUser, string dbPwd)
        {
            string conStr = GetConStr(db, serverIp, serverPort, dbName, dbUser, dbPwd);

            var con = db.Database.GetDbConnection();

            con.ConnectionString = conStr;

            return con;
        }

        public static string GetConStr(this Core.BlueDbContext db, string serverIp, string serverPort, string dbName, string dbUser, string dbPwd)
        {
            return $"Server={serverIp};Database={dbName};Port={serverPort};UserName={dbUser};pwd ={dbPwd}";
        }

        /// <summary>
        /// 判断数据库是否存在
        /// </summary>
        /// <param name="db"></param>
        /// <param name="con"></param>
        /// <returns></returns>
        public static bool DatabaseExists(this Core.BlueDbContext db, DbConnection con)
        {
            try
            {
                con.Open();
                return true;
            }
            catch
            {
                return false;
            }
            finally
            {
                con.Close();
            }
        }

        /// <summary>
        /// 执行ExecuteNonQuery
        /// </summary>
        /// <param name="db"></param>
        /// <param name="con"></param>
        /// <param name="commandType"></param>
        /// <param name="commandText"></param>
        /// <param name="parameterValues"></param>
        /// <returns></returns>
        public static int ExecuteNonQuery(this Core.BlueDbContext db, DbConnection con, CommandType commandType, string commandText, params DbParameter[] parameterValues)
        {
            using (DbCommand cmd = con.CreateCommand())
            {
                try
                {
                    con.Open();
                    cmd.CommandText = commandText;
                    cmd.CommandType = commandType;
                    if (parameterValues != null)
                    {
                        cmd.Parameters.AddRange(parameterValues);
                    }

                    return cmd.ExecuteNonQuery();
                }
                finally
                {
                    con.Close();
                }
            }
        }
    }
}